//
// Created by yangy on 2018/6/28.
//

#include "WIAudio.h"


WIAudio::WIAudio(WIPlayState *playState, int sample_rate, JavaCallBack *callBack)
        : callBack(callBack) {
    this->playState = playState;
    this->queueAudio = new WIQueue(playState);
    this->buffer = (uint8_t *) malloc(44100 * 2 * 2);
    this->callBack = callBack;
    this->sample_rate = sample_rate;

    sample_buffer = (SAMPLETYPE *) malloc(sample_rate * 2 * 2);
    soundTouch = new SoundTouch();
    soundTouch->setSampleRate(sample_rate);
    soundTouch->setChannels(2);
}

WIAudio::~WIAudio() {

}

void *decodePlay(void *data) {
    WIAudio *audio = (WIAudio *) data;
    audio->initopensl();
    pthread_exit((void *) audio->thread_play);
}


void WIAudio::play() {
    pthread_create(&thread_play, NULL, decodePlay, this);

}

int WIAudio::resampleAudio(void **out_buffer) {

    while (playState != NULL && !playState->exit) {

        if (playState->seek) {
            continue;
        }
        if (queueAudio->getQueueSize() == 0) {
            callBack->onLoad(CHILD_THREAD, true);
            playState->load = true;
            continue;
        } else {
            LOGI("queue size is %d", queueAudio->getQueueSize());
            if (playState->load) {
                playState->load = false;
                callBack->onLoad(CHILD_THREAD, false);
            }
        }
        LOGI("queue size is %d", queueAudio->getQueueSize());

        if (queueAudio->getQueueSize() > 0) {
            avPacket = av_packet_alloc();
            if (queueAudio->getAvPacket(avPacket) != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }

            ret = avcodec_send_packet(av_codec_ctx, avPacket);
            if (ret != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }

            avFrame = av_frame_alloc();
            ret = avcodec_receive_frame(av_codec_ctx, avFrame);
            if (ret == 0) {

                if (avFrame->channels > 0 && avFrame->channel_layout <= 0) {
                    avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);
                } else if (avFrame->channels <= 0 && avFrame->channel_layout > 0) {
                    avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
                }

                swrContext = swr_alloc_set_opts(
                        NULL,
                        AV_CH_LAYOUT_STEREO,
                        AV_SAMPLE_FMT_S16,
                        avFrame->sample_rate,
                        avFrame->channel_layout,
                        (AVSampleFormat) avFrame->format,
                        avFrame->sample_rate,
                        NULL,
                        NULL
                );

                if (swrContext == NULL || swr_init(swrContext) < 0) {
                    av_packet_free(&avPacket);
                    av_free(avPacket);
                    avPacket = NULL;
                    av_frame_free(&avFrame);
                    av_free(avFrame);
                    avFrame = NULL;

                    if (swrContext != NULL) {
                        swr_free(&swrContext);
                        swrContext = NULL;
                    }
                    continue;
                }
                nb = swr_convert(swrContext, &buffer, avFrame->nb_samples,
                                     (const uint8_t **) avFrame->data, avFrame->nb_samples
                );

                int channels_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);

                data_size = nb * channels_nb * 2;

                *out_buffer = buffer;

                new_time = avPacket->pts * av_q2d(time_base);

                if (new_time < clock) {
                    new_time = clock;
                }
                clock = new_time;


                LOGI("data size is %d", data_size);

                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;
                swr_free(&swrContext);
                swrContext = NULL;
                break;
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;
                continue;
            }

        }
    }
    return data_size;
}

int WIAudio::getsoundTouchDate() {

    while (playState != NULL && !playState->exit) {
        out_buffer = NULL;
        if (isFinished) {
            isFinished = false;
            data_size = resampleAudio((void **) &out_buffer);
            if (data_size > 0) {
                for (int i = 0; i < data_size / 2 + 1; ++i) {
                    sample_buffer[i] = out_buffer[i*2]|(out_buffer[i*2+1]<<8);
                }
                soundTouch->putSamples(sample_buffer,nb);
                num = soundTouch->receiveSamples(sample_buffer,data_size/4);
            } else{
                soundTouch->flush();
            }
        }
        if(num == 0)
        {
            isFinished = true;
            continue;
        } else{
            if(out_buffer == NULL)
            {
                num = soundTouch->receiveSamples(sample_buffer, data_size / 4);
                if(num == 0)
                {
                    isFinished = true;
                    continue;
                }
            }
            return num;
        }
    }
    return 0;
}

void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf simpleBufferQueueItf1, void *data) {
    WIAudio *audio = (WIAudio *) data;
    int size = audio->getsoundTouchDate()*4;
    audio->clock += size / ((double) audio->sample_rate * 2 * 2);
    if (audio->buffer != NULL && size > 0) {

        if (audio->record){
            audio->callBack->onRecord(CHILD_THREAD, size, (void *) audio->sample_buffer);
        }
        audio->callBack->onTimeInfo(CHILD_THREAD, audio->duration, audio->clock);
        //放入播放队列
        (*audio->simpleBufferQueueItf)->Enqueue(audio->simpleBufferQueueItf, (char *) audio->sample_buffer, size);
    }
}

void WIAudio::initopensl() {

    //初始化引擎对象并由对象得到接口
    slCreateEngine(&enginObject, 0, 0, 0, 0, 0);
    (*enginObject)->Realize(enginObject, SL_BOOLEAN_FALSE);
    (*enginObject)->GetInterface(enginObject, SL_IID_ENGINE, &engineItf);

    //需要做的声音处理功能数组
    const SLInterfaceID mid[1] = {SL_IID_ENVIRONMENTALREVERB};
    const SLboolean mird[1] = {SL_BOOLEAN_FALSE};
    (*engineItf)->CreateOutputMix(engineItf, &outputmixObject, 1, mid, mird);
    //得到上面声明的处理功能的接口
    (*outputmixObject)->Realize(outputmixObject, SL_BOOLEAN_FALSE);
    (*outputmixObject)->GetInterface(outputmixObject, SL_IID_ENVIRONMENTALREVERB,
                                     &outputEnvironmentalReverbItf);
    //混音器环境属性设置
    (*outputEnvironmentalReverbItf)->SetEnvironmentalReverbProperties(outputEnvironmentalReverbItf,
                                                                      &reverbSettings);

    //播放队列
    SLDataLocator_AndroidBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
    SLDataFormat_PCM format_pcm = {
            SL_DATAFORMAT_PCM, //格式
            2,//声道数
            (SLuint32) getresampleRate(sample_rate),//采样率
            SL_PCMSAMPLEFORMAT_FIXED_16,//采样位数 一定要与播放的pcm的一样 要不然可能快也可能会慢
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_FRONT_LEFT,//声道布局 前右|前左
            SL_BYTEORDER_LITTLEENDIAN
    };
    //播放源
    SLDataSource dataSource = {&android_queue, &format_pcm};
    //混音器
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputmixObject};
    //关联混音器
    SLDataSink audiosnk = {&outputMix, NULL};
    //要实现的功能
    const SLInterfaceID ids[4] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_MUTESOLO,SL_IID_PLAYBACKRATE};//队列播放
    const SLboolean irds[4] = {SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE,SL_BOOLEAN_FALSE};
    (*engineItf)->CreateAudioPlayer(engineItf, &pcmPlayerObject, &dataSource, &audiosnk, 2, ids,
                                    irds);
    (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);
    //创建播放接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &playItf);
    //得到Androidbufferqueue接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &simpleBufferQueueItf);
    //创建音量控制接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmVulumeItf);
    //左右声道控制接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_MUTESOLO, &pcmMuteSoloItf);

    //注册回掉函数
    (*simpleBufferQueueItf)->RegisterCallback(simpleBufferQueueItf, pcmBufferCallBack, this);
    //设置播放状态
    (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
    //第一次手动调用回掉函数 开始播放
    pcmBufferCallBack(simpleBufferQueueItf, this);
}

int WIAudio::getresampleRate(int sample_rate) {
    int rate = 0;
    switch (sample_rate) {
        case 8000:
            rate = SL_SAMPLINGRATE_8;
            break;
        case 11025:
            rate = SL_SAMPLINGRATE_11_025;
            break;
        case 12000:
            rate = SL_SAMPLINGRATE_12;
            break;
        case 16000:
            rate = SL_SAMPLINGRATE_16;
            break;
        case 22050:
            rate = SL_SAMPLINGRATE_22_05;
            break;
        case 24000:
            rate = SL_SAMPLINGRATE_24;
            break;
        case 32000:
            rate = SL_SAMPLINGRATE_32;
            break;
        case 44100:
            rate = SL_SAMPLINGRATE_44_1;
            break;
        case 48000:
            rate = SL_SAMPLINGRATE_48;
            break;
        case 64000:
            rate = SL_SAMPLINGRATE_64;
            break;
        case 88200:
            rate = SL_SAMPLINGRATE_88_2;
            break;
        case 96000:
            rate = SL_SAMPLINGRATE_96;
            break;
        case 192000:
            rate = SL_SAMPLINGRATE_192;
            break;
        default:
            rate = SL_SAMPLINGRATE_44_1;
    }
    return rate;
}

void WIAudio::releas() {
    //stop();
    if (queueAudio != NULL) {
        delete queueAudio;
        queueAudio = NULL;
    }

    if (pcmPlayerObject != NULL) {
        (*pcmPlayerObject)->Destroy(pcmPlayerObject);
        pcmPlayerObject = NULL;
        playItf = NULL;
        simpleBufferQueueItf = NULL;
    }

    if (outputmixObject != NULL) {
        (*outputmixObject)->Destroy(outputmixObject);
        outputmixObject = NULL;
        outputEnvironmentalReverbItf = NULL;
    }

    if (enginObject != NULL) {
        (*enginObject)->Destroy(enginObject);
        enginObject = NULL;
        engineItf = NULL;
    }

    if (buffer != NULL) {
        free(buffer);
        buffer = NULL;
    }

    if (av_codec_ctx != NULL) {
        avcodec_close(av_codec_ctx);
        avcodec_free_context(&av_codec_ctx);
        av_codec_ctx = NULL;
    }

    playState = NULL;
    callBack = NULL;
}

void WIAudio::pause() {
    if (playItf != NULL) {
        (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED);
    }
}

void WIAudio::resum() {
    if (playItf != NULL) {
        (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
    }
}

void WIAudio::stop() {
    if (playItf != NULL) {
        (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
    }
}

void WIAudio::setVolume(int percent) {
    volumePercent = percent;
    if (pcmVulumeItf != NULL) {
        if (percent > 30) {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -20);
        } else if (percent > 25) {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -22);
        } else if (percent > 20) {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -25);
        } else if (percent > 15) {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -28);
        } else if (percent > 10) {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -30);
        } else if (percent > 5) {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -34);
        } else if (percent > 3) {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -37);
        } else if (percent > 0) {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -40);
        } else {
            (*pcmVulumeItf)->SetVolumeLevel(pcmVulumeItf, (100 - percent) * -100);
        }
    }
}

void WIAudio::setMute(int mute) {
    if (pcmMuteSoloItf == NULL)
        return;
    if (mute == RIGHT_CHANNEL) {
        (*pcmMuteSoloItf)->GetChannelMute(pcmMuteSoloItf, 0, (SLboolean *) true);
        (*pcmMuteSoloItf)->GetChannelMute(pcmMuteSoloItf, 1, (SLboolean *) false);
    } else if (mute == LEFT_CHANNEL) {
        (*pcmMuteSoloItf)->GetChannelMute(pcmMuteSoloItf, 1, (SLboolean *) true);
        (*pcmMuteSoloItf)->GetChannelMute(pcmMuteSoloItf, 0, (SLboolean *) false);
    } else {
        (*pcmMuteSoloItf)->GetChannelMute(pcmMuteSoloItf, 1, (SLboolean *) true);
        (*pcmMuteSoloItf)->GetChannelMute(pcmMuteSoloItf, 0, (SLboolean *) true);
    }
}

void WIAudio::setpitch(double pitch) {
     if(soundTouch!=NULL){
         soundTouch->setPitch(pitch);
     }
}

void WIAudio::settempo(double tempo) {
     if (soundTouch!=NULL){
         soundTouch->setTempo(tempo);
     }
}




